# Rsbuild Instance

本章节描述了 Rsbuild 实例对象上所有的属性和方法。

## rsbuild.context

`rsbuild.context` 是一个只读对象，提供一些上下文信息。

### context.version

当前使用的 `@rsbuild/core` 版本。

- **类型：**

```ts
type Version = string;
```

### context.rootPath

当前执行构建的根路径，对应调用 [createRsbuild](/api/javascript-api/core#creatersbuild) 时传入的 `cwd` 选项。

- **类型：**

```ts
type RootPath = string;
```

### context.distPath

构建产物输出目录的绝对路径，对应 `RsbuildConfig` 中的 [output.distPath.root](/config/output/dist-path) 配置项。

当有多个环境时，Rsbuild 会尝试获取所有环境的父 distPath 作为 `context.distPath`。

如果要获取指定环境的输出目录的绝对路径，建议使用 [environment.distPath](/api/javascript-api/environment-api#distpath)。

- **类型：**

```ts
type DistPath = string;
```

### context.cachePath

构建过程中生成的缓存文件所在的绝对路径。

- **类型：**

```ts
type CachePath = string;
```

### context.devServer

Dev server 相关信息，包含了当前 dev server 的 hostname 和端口号。

- **类型：**

```ts
type DevServer = {
  hostname: string;
  port: number;
};
```

### context.bundlerType

当前执行构建的构建工具类型。

- **类型：**

```ts
type bundlerType = 'rspack' | 'webpack';
```

> Rsbuild 内部支持切换到 webpack 进行对照测试，因此提供了该字段进行区分，通常你不需要使用此字段。

## rsbuild.environments

### target

构建产物类型，对应 Rsbuild 的 [output.target](/config/output/target) 配置。

- **类型：**

```ts
type RsbuildTarget = 'web' | 'node' | 'web-worker';
```

### tsconfigPath

tsconfig.json 文件的绝对路径，若项目中不存在 tsconfig.json 文件，则为 `undefined`。

- **类型：**

```ts
type TsconfigPath = string | undefined;
```

## rsbuild.build

调用 build 方法时，会执行一次生产模式构建。

- **类型：**

```ts
type BuildOptions = {
  /**
   * 是否监听文件变化并重新构建
   *
   * @default false
   */
  watch?: boolean;
  /**
   * 使用自定义的 Rspack Compiler 对象
   */
  compiler?: Compiler | MultiCompiler;
};

function Build(options?: BuildOptions): Promise<{
  /**
   * Rspack 的 [stats](https://rspack.dev/api/javascript-api/stats) 对象。
   */
  stats?: Rspack.Stats | Rspack.MultiStats;
  /**
   * 停止监听文件变化。
   */
  close: () => Promise<void>;
}>;
```

- **示例：**

```ts
import { logger } from '@rsbuild/core';

// Example 1: run build
await rsbuild.build();

// Example 2: build and handle the error
try {
  await rsbuild.build();
} catch (err) {
  logger.error('Failed to build.');
  logger.error(err);
  process.exit(1);
}

// Example 3: build and get all stats
const { stats } = await rsbuild.build();

if (stats) {
  const { assets } = stats.toJson({ assets: true });
  console.log(assets);
}
```

### 监听文件变化

如果需要自动监听文件变化并重新执行构建，可以将 `watch` 参数设置为 `true`。

```ts
await rsbuild.build({
  watch: true,
});
```

在 watch 模式下，`rsbuild.build()` 会返回一个 `close` 方法，调用该方法将会结束监听：

```ts
const result = await rsbuild.build({
  watch: true,
});

await result.close();
```

### Stats 对象

在非 watch 模式下，`rsbuild.build()` 会返回一个 Rspack 的 [stats](https://rspack.dev/api/javascript-api/stats) 对象：

```ts
const result = await rsbuild.build();

console.log(result.stats);
```

### 自定义 Compiler

个别情况下，你可能希望使用自定义的 compiler：

```ts
import { rspack } from '@rsbuild/core';

const compiler = rspack({
  // ...
});
await rsbuild.build({
  compiler,
});
```

## rsbuild.startDevServer

启动本地 dev server。

- **类型：**

```ts
type StartDevServerOptions = {
  /**
   * 使用自定义的 Rspack Compiler 对象
   */
  compiler?: Compiler | MultiCompiler;
  /**
   * 是否在启动时静默获取端口号，不输出任何日志
   * @default false
   */
  getPortSilently?: boolean;
};

type StartServerResult = {
  urls: string[];
  port: number;
  server: Server;
};

function StartDevServer(
  options?: StartDevServerOptions,
): Promise<StartServerResult>;
```

- **示例：**

启动 dev server：

```ts
import { logger } from '@rsbuild/core';

// Start dev server
await rsbuild.startDevServer();

// Start dev server and handle the error
try {
  await rsbuild.startDevServer();
} catch (err) {
  logger.error('Failed to start dev server.');
  logger.error(err);
  process.exit(1);
}
```

成功启动 dev server 后，可以看到以下日志信息：

```
  ➜ Local:    http://localhost:3000
  ➜ Network:  http://192.168.0.1:3000
```

`startDevServer` 会返回以下参数：

- `urls`：访问 dev server 的 URLs
- `port` 实际监听的端口号
- `server`：Server 实例对象

```ts
const { urls, port, server } = await rsbuild.startDevServer();
console.log(urls); // ['http://localhost:3000', 'http://192.168.0.1:3000']
console.log(port); // 3000

// 关闭 dev server
await server.close();
```

### 自定义 Compiler

个别情况下，你可能希望使用自定义的 compiler：

```ts
import { rspack } from '@rsbuild/core';

const compiler = rspack({
  // ...
});
await rsbuild.startDevServer({
  compiler,
});
```

### 静默获取端口号

某些情况下，默认启动的端口号已经被占用，此时 Rsbuild 会自动递增端口号，直至找到一个可用端口。这个过程会输出提示日志，如果你不希望这段日志，可以将 `getPortSilently` 设置为 `true`。

```ts
await rsbuild.startDevServer({
  getPortSilently: true,
});
```

## rsbuild.createDevServer

Rsbuild 配备了一个内置的开发服务器，当你执行 `rsbuild dev` 时，将启动 Rsbuild dev server，并提供页面预览、路由、模块热更新等功能。

如果你希望将 Rsbuild dev server 集成到自定义的 server 中，可以通过该方法获取 dev server 的实例方法，按需进行调用。

- **类型：**

```ts
type EnvironmentAPI = {
  [name: string]: {
    /**
     * 获取当前环境的构建信息
     */
    getStats: () => Promise<Stats>;
    /**
     * 在服务端加载并执行构建产物
     *
     * @param entryName - 入口名称，和 Rsbuild source.entry 的某一个 key 值对应
     * @returns 入口模块的返回值
     */
    loadBundle: <T = unknown>(entryName: string) => Promise<T>;
    /**
     * 获取编译后的 HTML 模版内容
     */
    getTransformedHtml: (entryName: string) => Promise<string>;
  };
};

type RsbuildDevServer = {
  /**
   * 监听 Rsbuild server
   * 当你使用自定义 server 时，不需要调用该方法
   */
  listen: () => Promise<{
    port: number;
    urls: string[];
    server: {
      close: () => Promise<void>;
    };
  }>;
  /**
   * Rsbuild server 提供的 environment API
   */
  environments: EnvironmentAPI;
  /**
   * 解析后的端口号
   * 默认情况下，Rsbuild Server 会监听 `3000` 端口，如果端口被占用，则自动递增端口号
   */
  port: number;
  /**
   * `connect` 实例
   * 可用于向 dev server 附加自定义中间件
   */
  middlewares: Connect.Server;
  /**
   * 通知 Rsbuild 自定义 Server 已启动
   * Rsbuild 会在此阶段触发 `onAfterStartDevServer` 钩子
   */
  afterListen: () => Promise<void>;
  /**
   * 激活 socket 连接
   * 这确保了 HMR 正常工作
   */
  connectWebSocket: (options: { server: HTTPServer }) => void;
  /**
   * 关闭 Rsbuild server
   */
  close: () => Promise<void>;
  /**
   * 打印 server URLs
   */
  printUrls: () => void;
  /**
   * 启动服务器后，在浏览器中打开 URL
   */
  open: () => Promise<void>;
};

type CreateDevServerOptions = {
  /**
   * 自定义 Compiler 对象
   */
  compiler?: Compiler | MultiCompiler;
  /**
   * 是否在启动时静默获取端口号，不输出任何日志
   * @default false
   */
  getPortSilently?: boolean;
  /**
   * 是否触发 Rsbuild 编译
   * @default true
   */
  runCompile?: boolean;
};

function CreateDevServer(
  options?: CreateDevServerOptions,
): Promise<RsbuildDevServer>;
```

- **示例：**

下面是一个在 [express](https://expressjs.com/) 中集成 Rsbuild DevServer 的例子:

```ts
import { createRsbuild } from '@rsbuild/core';
import express from 'express';

export async function startDevServer() {
  // 初始化 Rsbuild
  const rsbuild = await createRsbuild({});

  const app = express();

  // 创建 Rsbuild DevServer 实例
  const rsbuildServer = await rsbuild.createDevServer();

  // 使用 Rsbuild 的内置中间件
  app.use(rsbuildServer.middlewares);

  const httpServer = app.listen(rsbuildServer.port, async () => {
    // 通知 Rsbuild 自定义 Server 已启动
    await rsbuildServer.afterListen();
  });

  rsbuildServer.connectWebSocket({ server: httpServer });
}
```

详细使用情况可参考：[示例](https://github.com/rspack-contrib/rspack-examples/blob/main/rsbuild/express/server.mjs)。

如果你希望直接使用 Rsbuild DevServer 启动项目，可以直接使用 [Rsbuild - startDevServer](#rsbuildstartdevserver) 方法。 `startDevServer` 实际上是以下代码的语法糖：

```ts
const server = await rsbuild.createDevServer();

await server.listen();
```

### connectWebSocket

Rsbuild 内置了 WebSocket 处理器以支持 HMR 功能：

1. 当用户通过浏览器访问页面时，会自动向服务器发起 WebSocket 连接请求。
2. Rsbuild 开发服务器检测到连接请求后，会指示内置的 WebSocket 处理器进行处理。
3. 浏览器与 Rsbuild WebSocket 处理器成功建立连接后，便可进行实时通信。
4. 每次重新编译完成后，Rsbuild WebSocket 处理器会通知浏览器。随后，浏览器向开发服务器发送 `hot-update.(js|json)` 请求，以加载编译后的新模块。

当你使用自定义的 server 时，可能会遇到 HMR 连接报错的问题，这是因为自定义的 server 没有将 WebSocket 连接请求转发给 Rsbuild 的 WebSocket 处理器。此时需要通过 `connectWebSocket` 方法已使 Rsbuild 能够感知并处理来自浏览器的 WebSocket 连接请求。

```ts
const rsbuildServer = await rsbuild.createDevServer();

const httpServer = app.listen(rsbuildServer.port);

rsbuildServer.connectWebSocket({ server: httpServer });
```

## rsbuild.preview

在本地启动 server 来预览生产模式构建的产物，需要在 [rsbuild.build](#rsbuildbuild) 方法之后执行。

- **类型：**

```ts
type PreviewOptions = {
  /**
   * 是否在启动时静默获取端口号，不输出任何日志
   * @default false
   */
  getPortSilently?: boolean;
  /**
   * 是否检查 dist 目录存在且不为空
   * @default true
   */
  checkDistDir?: boolean;
};

type StartServerResult = {
  urls: string[];
  port: number;
  server: {
    close: () => Promise<void>;
  };
};

function preview(options?: PreviewOptions): Promise<StartServerResult>;
```

- **示例：**

启动 Server：

```ts
import { logger } from '@rsbuild/core';

// Start preview server
await rsbuild.preview();

// Start preview server and handle the error
try {
  await rsbuild.preview();
} catch (err) {
  logger.error('Failed to start preview server.');
  logger.error(err);
  process.exit(1);
}
```

`preview` 会返回以下参数：

- `urls`：访问 Server 的 URLs
- `port` 实际监听的端口号
- `server`：Server 实例对象

```ts
const { urls, port, server } = await rsbuild.preview();
console.log(urls); // ['http://localhost:3000', 'http://192.168.0.1:3000']
console.log(port); // 3000

// 关闭 Server
await server.close();
```

## rsbuild.createCompiler

创建一个 Rspack [Compiler](https://rspack.dev/api/javascript-api/compiler) 实例；如果本次构建存在多个 [environments](/config/environments)，则返回值为 `MultiCompiler`。

- **类型：**

```ts
function CreateCompiler(): Promise<Compiler | MultiCompiler>;
```

- **示例：**

```ts
const compiler = await rsbuild.createCompiler();
```

> 大部分场景下，你不需要使用该 API，除非需要进行自定义 dev server 等高级操作。

## rsbuild.addPlugins

注册一个或多个 Rsbuild 插件，可以被多次调用。

该方法需要在开始编译前调用，如果在开始编译之后调用，则不会影响编译结果。

- **类型：**

```ts
type AddPluginsOptions = { before?: string; environment?: string };

function AddPlugins(
  plugins: Array<RsbuildPlugin | Falsy>,
  options?: AddPluginsOptions,
): void;
```

- **示例：**

```ts
rsbuild.addPlugins([pluginFoo(), pluginBar()]);

// 在 bar 插件之前插入
rsbuild.addPlugins([pluginFoo()], { before: 'bar' });

// 为 node 环境添加插件
rsbuild.addPlugins([pluginFoo()], { environment: 'node' });
```

## rsbuild.getPlugins

获取当前 Rsbuild 实例中注册的所有 Rsbuild 插件。

- **类型：**

```ts
function GetPlugins(): RsbuildPlugin[];
```

- **示例：**

```ts
console.log(rsbuild.getPlugins());
```

## rsbuild.removePlugins

移除一个或多个 Rsbuild 插件，可以被多次调用。

该方法需要在开始编译前调用，如果在开始编译之后调用，则不会影响编译结果。

- **类型：**

```ts
function RemovePlugins(pluginNames: string[]): void;
```

- **示例：**

```ts
// 添加插件
const pluginFoo = pluginFoo();
rsbuild.addPlugins(pluginFoo);

// 移除插件
rsbuild.removePlugins([pluginFoo.name]);
```

## rsbuild.isPluginExists

import IsPluginExists from '@zh/shared/isPluginExists.mdx';

<IsPluginExists />

- **示例：**

```ts
rsbuild.addPlugins([pluginFoo()]);

rsbuild.isPluginExists(pluginFoo().name); // true
```

## rsbuild.initConfigs

initConfigs 方法用于初始化 Rsbuild 内部的配置，并返回 Rsbuild 内部生成的 Rspack 配置。

通常你不需要调用 initConfigs 方法，因为调用 [rsbuild.build](#rsbuildbuild)、[rsbuild.startDevServer](#rsbuildstartdevserver) 等方法时会自动调用 initConfigs。

- **类型：**

```ts
function InitConfigs(): Promise<{
  rspackConfigs: Rspack.Configuration[];
}>;
```

- **示例：**

```ts
const { rspackConfigs } = await rsbuild.initConfigs();

console.log(rspackConfigs);
```

## rsbuild.inspectConfig

inspectConfig 方法通常用于调试 Rsbuild 内部的配置，它会返回 Rsbuild 内部生成的 Rsbuild 配置和 Rspack 配置，将它们序列化为字符串，并支持写入到磁盘上。

如果你需要在构建过程中查看 Rsbuild 和 Rspack 配置，可以使用 [调试模式](/guide/debug/debug-mode)，也可以通过 [onBeforeBuild](#rsbuildonbeforebuild)、[onBeforeCreateCompile](#rsbuildonbeforecreatecompiler) 等 hooks 来获取。

- **类型：**

```ts
type InspectConfigOptions = {
  // 查看指定环境下的配置
  // 默认为 "development"，可以设置为 "production"
  mode?: RsbuildMode;
  // 是否开启冗余模式，展示配置中函数的完整内容
  // 默认为 `false`
  verbose?: boolean;
  // 指定输出路径
  // 默认为 `output.distPath.root` 的值
  outputPath?: string;
  // 是否将结果写入到磁盘中
  // 默认为 `false`
  writeToDisk?: boolean;
};

async function InspectConfig(options?: InspectConfigOptions): Promise<{
  rsbuildConfig: string;
  bundlerConfigs: string[];
  environmentConfigs: string[];
  origin: {
    rsbuildConfig: RsbuildConfig;
    environmentConfigs: Record<string, EnvironmentConfig>;
    bundlerConfigs: BundlerConfigs[];
  };
}>;
```

### 示例

拿到字符串格式的 configs 内容：

```ts
const { rsbuildConfig, bundlerConfigs } = await rsbuild.inspectConfig();

console.log(rsbuildConfig, bundlerConfigs);
```

直接将配置内容写入到磁盘上：

```ts
await rsbuild.inspectConfig({
  writeToDisk: true,
});
```

### 输出路径

你可以通过 `outputPath` 来设置输出目录，默认为 [output.distPath.root](/config/output/dist-path) 的值。

当 `outputPath` 是一个相对路径时，会相对于 `output.distPath.root` 的值进行拼接。你也可以将 `outputPath` 设置为一个绝对路径，此时会直接将文件写入到该路径下。比如：

```ts
import path from 'node:path';

await rsbuild.inspectConfig({
  writeToDisk: true,
  outputPath: path.join(__dirname, 'custom-dir'),
});
```

## rsbuild.onBeforeCreateCompiler

import OnBeforeCreateCompiler from '@zh/shared/onBeforeCreateCompiler.mdx';

<OnBeforeCreateCompiler />

- **示例：**

```ts
rsbuild.onBeforeCreateCompiler(({ bundlerConfigs }) => {
  console.log('the Rspack config is ', bundlerConfigs);
});
```

## rsbuild.onAfterCreateCompiler

import OnAfterCreateCompiler from '@zh/shared/onAfterCreateCompiler.mdx';

<OnAfterCreateCompiler />

- **示例：**

```ts
rsbuild.onAfterCreateCompiler(({ compiler }) => {
  console.log('the compiler is ', compiler);
});
```

## rsbuild.onBeforeBuild

import OnBeforeBuild from '@zh/shared/onBeforeBuild.mdx';

<OnBeforeBuild />

- **示例：**

```ts
rsbuild.onBeforeBuild(({ bundlerConfigs }) => {
  console.log('the Rspack config is ', bundlerConfigs);
});
```

## rsbuild.onAfterBuild

import OnAfterBuild from '@zh/shared/onAfterBuild.mdx';

<OnAfterBuild />

- **示例：**

```ts
rsbuild.onAfterBuild(({ stats }) => {
  console.log(stats?.toJson());
});
```

## rsbuild.onCloseBuild

import OnCloseBuild from '@zh/shared/onCloseBuild.mdx';

<OnCloseBuild />

- **示例：**

```ts
rsbuild.onCloseBuild(async () => {
  console.log('close build!');
});
```

## rsbuild.onBeforeStartDevServer

import OnBeforeStartDevServer from '@zh/shared/onBeforeStartDevServer.mdx';

<OnBeforeStartDevServer />

- **示例：**

```ts
rsbuild.onBeforeStartDevServer(() => {
  console.log('before start!');
});
```

## rsbuild.onAfterStartDevServer

import OnAfterStartDevServer from '@zh/shared/onAfterStartDevServer.mdx';

<OnAfterStartDevServer />

- **示例：**

```ts
rsbuild.onAfterStartDevServer(({ port, routes }) => {
  console.log('this port is: ', port);
  console.log('this routes is: ', routes);
});
```

## rsbuild.onCloseDevServer

import OnCloseDevServer from '@zh/shared/onCloseDevServer.mdx';

<OnCloseDevServer />

- **示例：**

```ts
rsbuild.onCloseDevServer(async () => {
  console.log('close dev server!');
});
```

## rsbuild.onBeforeStartProdServer

import OnBeforeStartProdServer from '@zh/shared/onBeforeStartProdServer.mdx';

<OnBeforeStartProdServer />

- **示例：**

```ts
rsbuild.onBeforeStartProdServer(() => {
  console.log('before start!');
});
```

## rsbuild.onAfterStartProdServer

import OnAfterStartProdServer from '@zh/shared/onAfterStartProdServer.mdx';

<OnAfterStartProdServer />

- **示例：**

```ts
rsbuild.onAfterStartProdServer(({ port, routes }) => {
  console.log('this port is: ', port);
  console.log('this routes is: ', routes);
});
```

## rsbuild.onDevCompileDone

import OnDevCompileDone from '@zh/shared/onDevCompileDone.mdx';

<OnDevCompileDone />

- **示例：**

```ts
rsbuild.onDevCompileDone(({ isFirstCompile }) => {
  if (isFirstCompile) {
    console.log('first compile!');
  } else {
    console.log('re-compile!');
  }
});
```

## rsbuild.onExit

import OnExit from '@zh/shared/onExit.mdx';

<OnExit />

- **示例：**

```ts
rsbuild.onExit(() => {
  console.log('exit!');
});
```

## rsbuild.getRsbuildConfig

import GetRsbuildConfig from '@zh/shared/getRsbuildConfig.mdx';

<GetRsbuildConfig />

- **示例：**

```ts
rsbuild.onBeforeBuild(() => {
  const config = rsbuild.getRsbuildConfig();
  console.log(config.html?.title);
});
```

## rsbuild.getNormalizedConfig

import GetNormalizedConfig from '@zh/shared/getNormalizedConfig.mdx';

<GetNormalizedConfig />

- **示例：**

```ts
rsbuild.onBeforeBuild(() => {
  const config = rsbuild.getNormalizedConfig();
  console.log(config.html.title);
});
```
